/**
 * Copyright (c) 2012 Nokia Corporation. 
 */

package com.nokia.example.tictactoe.connectivity;

import java.io.UnsupportedEncodingException;

/**
 * A UDP protocol based server for automatic peer discovery.
 */
public class DiscoveryServer extends UdpServer implements Connection.Listener {
	// Constants
	public static final Integer DISCOVERY_PORT = new Integer(13002);
	public static final String IDENTIFIER = new String("TICTAC");
	
	// Members
	private TcpClient _tcpClient = null;
	private boolean _firstMessage;
	
	/**
	 * Constructor.
	 * @param tcpClient The TCP client instance.
	 */
	public DiscoveryServer(TcpClient tcpClient) {
		super();
		addListener(this);
		_tcpClient = tcpClient;
		_firstMessage = true;
	}
	
	/**
	 * From UdpServer.
	 */
	/*public void close() {
		super.close();
		setState(NOT_CONNECTED);
	}*/
	
	// Methods from Connection.Listener. This instance is its own listener.
	public void onStateChanged(int state) {}
	public void onPeerDisconnected() {}
	public void onMessageSent(byte[] message) {}
	public void onTimeout() {}
	public void onError(String errorMessage) {}
	
	/**
	 * From Connection.Listener.
	 */
	public void onReceived(byte[] message) {
		if (!_firstMessage) {
			// We're either currently processing a message or the message
			// received earlier was accepted. Reject this message.
			return;
		}
		
		String msg = null;
		
		try {
			msg = new String(message, "UTF8");
		}
		catch (UnsupportedEncodingException e) {
			System.out.println("DiscoveryServer::onReceived(): " + e.toString());
			return;
		}
		
		log("Rv: " + msg);
		
		if (msg.startsWith(IDENTIFIER)) {
			_firstMessage = false;
			
			// Try to open the connection
			boolean success = false;
			
			if (extractPeerAddress(msg) == true) {
				if (_peerAddress.equals(DiscoveryClient.localAddress())) {
					// The sender of the message received seems to be
					// ourselves. Do not accept this connection.
					log("Rejecting the peer; it seems to be myself: " + _peerAddress);
				}
				else {
					System.out.println("DiscoveryServer::onReceived(): Trying to connect to " + _peerAddress);
					
					if (_tcpClient != null) {
						success = _tcpClient.open(_peerAddress,
									ConnectionManager.SERVER_PORT);
					}
					else {
						System.out.println("DiscoveryServer::onReceived(): No TCP client!");
					}
				}
			}
			else {
				// Failed to resolve the peer address
				log("DiscoveryServer::onReceived(): Failed to resolve the peer address!");
			}
			
			if (success == false) {
				// Failed to connect, keep listening for new broadcasts				
				_firstMessage = true;
				
				if (_peerAddress == null
						|| !_peerAddress.equals(DiscoveryClient.localAddress()))
				{
					System.out.println("DiscoveryServer::onReceived(): Failed to connect.");
				}
			}
		}	
	}
	
	/**
	 * Starts listening for messages.
	 * @return True if successful, false otherwise.
	 */
	public boolean startListening() {
		_firstMessage = true;
		boolean retval = open(null, DISCOVERY_PORT);
		
		if (retval) {
			log("Discovery server started");
		}
		
		return retval;
	}
	
	/**
	 * Extracts the peer address (IP) from the given message.
	 * @param message The message containing the peer address.
	 * @return True if the address was extracted successfully, false otherwise.
	 */
	private boolean extractPeerAddress(String message) {
		int tempIndex = message.lastIndexOf(':');
		
		if (tempIndex <= 6) {
			return false;
		}
		
		String noPort = message.substring(0, tempIndex);
		char ch;
		int numOfPoints = 0;
		
		for (int i = noPort.length() - 1; i >= 0; --i) {
			ch = noPort.charAt(i);
			
			if (ch != '.' && Character.isDigit(ch) == false) {
				// Not part of an IP address since the character is not a
				// point nor a number
				break;
			}
			
			if (ch == '.') {
				numOfPoints++;
			}
			
			tempIndex = i;
		}
		
		if (numOfPoints == 3 && noPort.length() - 1 - tempIndex >= 6) {
			_peerAddress = noPort.substring(tempIndex, noPort.length());
			return true;
		}
		
		return false;
	}
}
